//
//  API.swift
//  EngNews
//
//  Created by tiger on 2017/4/14.
//  Copyright © 2017年 tiger. All rights reserved.
//

import UIKit
import Alamofire

public class QxRequestError: Error {
    public var code: Int = 0
    public var domain: String? = ""
    public var localizedDescription: String? = ""
    
    init(code: Int, domain: String?,localizedDescription: String?) {
        self.code = code
        self.domain = domain
        self.localizedDescription = localizedDescription
    }
}

public protocol QxRequestProtocol {
    func perform(withSuccess successBlock: (([String: Any]) -> Void)?, failure failureBlock: ((QxRequestError) -> Void)?) -> Alamofire.DataRequest?
}

public class APIRequest: QxRequestProtocol {

    var request: Alamofire.DataRequest!
    
    private var isCache: Bool = false
    private var isNetworkActivityIndicatorVisible: Bool = false
    
    func networkActivityIndicatorVisible() -> APIRequest {
        self.isNetworkActivityIndicatorVisible = true
        return self
    }
    
    func cache() -> APIRequest {
        self.isCache = true
        return self
    }
    
    public func perform(withSuccess successBlock: (([String: Any]) -> Void)? = nil, failure failureBlock: ((QxRequestError) -> Void)? = nil ) -> Alamofire.DataRequest? {
        if isCache, let request =  self.request.request {
            if let cachedResponseData = APICache.shared.cache.cachedResponse(for: request)?.data {
                do {
                    let json = try JSONSerialization.jsonObject(with: cachedResponseData, options: .allowFragments)
                    let result = json as! [String:Any]
                    successBlock?(result)
                } catch {
                    
                }
            }
        }
        
        if self.request == nil {
            failureBlock?(QxRequestError(code: 201, domain: "request error", localizedDescription: "request generate error"))
            return self.request
        }
        
        if self.isNetworkActivityIndicatorVisible {
            UIApplication.shared.isNetworkActivityIndicatorVisible = true
        }
        self.request.responseJSON { response in
            if self.isNetworkActivityIndicatorVisible {
                UIApplication.shared.isNetworkActivityIndicatorVisible = false
            }
            switch response.result {
            case .success(let json):
                let result = json as! [String:Any]
                successBlock?(result)
                if self.isCache {
                    let cachedResponse = CachedURLResponse(response: response.response!, data: response.data!)
                    APICache.shared.cache.storeCachedResponse(cachedResponse, for: self.request.request!)
                }
                break
            case .failure(let error):
                failureBlock?(QxRequestError(code: 201, domain: "数据错误", localizedDescription: error.localizedDescription))
                break
            }
        }
        return self.request
    }
}

public class DownloadAPIRequest: APIRequest {
    
}

public class UploadAPIRequest: APIRequest {
    
}

public struct API {
    
    public struct Config {
        static var baseUrl: String = ""
        static var headers: HTTPHeaders?
    }
    
    public enum RequestMethod: String {
        case options = "OPTIONS"
        case get     = "GET"
        case head    = "HEAD"
        case post    = "POST"
        case put     = "PUT"
        case patch   = "PATCH"
        case delete  = "DELETE"
        case trace   = "TRACE"
        case connect = "CONNECT"
        
        public func asHttpMethod() -> Alamofire.HTTPMethod {
            switch self {
            case .post:
                return Alamofire.HTTPMethod.post
            case .get:
                return Alamofire.HTTPMethod.get
            default:
                return Alamofire.HTTPMethod.get
            }
        }
    }
}

public extension API {
    
    fileprivate struct FactoryConvertible: Alamofire.URLConvertible {
    
        public var path: String = ""
        
        init(path: String) {
            self.path = path
        }
        
        public func asURL() throws -> URL {
            if let url = URL(string: API.Config.baseUrl+path) {
                return url
            }
            throw QxRequestError(code: 201, domain: "reqeust error", localizedDescription: "request url invalid")
        }
    }
    
    public struct Factory {
        
        public static func request(path: String,parameters: [String: Any],method: API.RequestMethod) -> APIRequest {
            let request = APIRequest()
            request.request = Alamofire.request(FactoryConvertible(path: path),method: method.asHttpMethod(),parameters: parameters,headers: API.Config.headers)
            return request
        }
        
        public static func downloadRequest(path: String,parameters: [String: Any],method: API.RequestMethod) -> DownloadAPIRequest {
            let request = DownloadAPIRequest()
            request.request = Alamofire.request(FactoryConvertible(path: path),method: method.asHttpMethod(),parameters: parameters,headers: API.Config.headers)
            return request
        }
        
        public static func uploadRequest(path: String,parameters: [String: Any],method: API.RequestMethod) -> UploadAPIRequest {
            let request = UploadAPIRequest()
            request.request = Alamofire.request(FactoryConvertible(path: path),method: method.asHttpMethod(),parameters: parameters,headers: API.Config.headers)
            return request
        }
    }
}

public class APICache: NSObject {
    
    public static let shared = APICache()
    
    public var cache: URLCache!
    
    override init() {
        super.init()
        let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] + "/QxNetworkCache"
        var directory: ObjCBool = ObjCBool(true)
        if FileManager.default.fileExists(atPath: path, isDirectory: &directory) == false {
            do {
                try FileManager.default.createDirectory(at: URL(fileURLWithPath: path), withIntermediateDirectories: false, attributes: nil)
            } catch {
                
            }
        }
        self.cache = URLCache(memoryCapacity: 500*1024*1024, diskCapacity: 500*1024*1024, diskPath: path)
    }
}


